package com.gcloud.api.filter;

import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StreamUtils;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
/**
Copyright (c) [2020] [G-CLOUD TECHNOLOGY] [G-Cloud 8.0] is licensed under the Mulan PSL v1.
You can use this software according to the terms and conditions of the Mulan PSL v1. You may obtain
a copy of Mulan PSL v1 at: http://license.coscl.org.cn/MulanPSL THIS SOFTWARE IS PROVIDED ON AN 
"AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 
TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the Mulan PSL v1 for more details.
 */


@Component
public class RequestRouter {
	public void route(String targetUrl,HttpServletRequest req, HttpServletResponse resp) {
		RequestEntity<byte[]> requestEntity = null;
		try {
			requestEntity = createRequestEntity(req, targetUrl);
		} catch (Exception e) {
			e.printStackTrace();
		}

		MyRestTemplate restTemplate = new MyRestTemplate();
		ResponseEntity responseEntity = restTemplate.exchange(requestEntity, byte[].class);
		try {
			addResponseHeaders(resp,responseEntity);
			writeResponse(resp,responseEntity);
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	private RequestEntity createRequestEntity(HttpServletRequest request, String url)
			throws URISyntaxException, IOException {
		String method = request.getMethod();
		HttpMethod httpMethod = HttpMethod.resolve(method);
		MultiValueMap<String, String> headers = createRequestHeaders(request); // 1、封装请求头
		MultiValueMap<String, Object> params = createRequestParams(request);
		byte[] body = createRequestBody(request); // 2、封装请求体
		String queryStr = request.getQueryString();//URL参数
		if(null != queryStr && queryStr.length()>0) {
			url +="?"+ queryStr;
		}
		// 3、构造出RestTemplate能识别的RequestEntity
		RequestEntity requestEntity = null;
		if(params != null && params.size() > 0){
			requestEntity = new RequestEntity(params, headers, httpMethod, new URI(url));
		}else{
			requestEntity = new RequestEntity(body, headers, httpMethod, new URI(url));
		}
		return requestEntity;
	}

	private byte[] createRequestBody(HttpServletRequest request) throws IOException {
		InputStream inputStream = request.getInputStream();
		return StreamUtils.copyToByteArray(inputStream);
	}

	private MultiValueMap<String, String> createRequestHeaders(HttpServletRequest request) {
		HttpHeaders headers = new HttpHeaders();
		List<String> headerNames = Collections.list(request.getHeaderNames());
		for (String headerName : headerNames) {
			List<String> headerValues = Collections.list(request.getHeaders(headerName));
			for (String headerValue : headerValues) {
				headers.add(headerName, headerValue);
			}
		}

		String method = request.getMethod();
		String contentType = request.getContentType();

		if("post".equalsIgnoreCase(method) && contentType.toLowerCase(Locale.ENGLISH).startsWith("multipart/")){
			headers.setContentType(MediaType.MULTIPART_FORM_DATA);
		}

		return headers;
	}

	private MultiValueMap<String, Object> createRequestParams(HttpServletRequest request){

		MultiValueMap<String, Object> result = null;
		Map<String, String[]> params = request.getParameterMap();
		if(params != null && params.size() > 0){
			result = new LinkedMultiValueMap<>();
			Set<String> keys = params.keySet();
			for(String key : keys){

				String[] valueArr = params.get(key);
				if(valueArr == null){
					result.add(key, valueArr);
				}else if(valueArr.length == 0){
					result.add(key, "");
				}else if(valueArr.length == 1){
					result.add(key, valueArr[0]);
				}else if(valueArr.length > 1){
					for(String val : valueArr){
						result.add(key, val);
					}

				}
			}
		}

		String method = request.getMethod();
		String contentType = request.getContentType();

		if("post".equalsIgnoreCase(method) && contentType.toLowerCase(Locale.ENGLISH).startsWith("multipart/")){

			try{
				if(request.getParts() != null && request.getParts().size() > 0){
					for(Part part : request.getParts()){
						if(result.containsKey(part.getName())){
							continue;
						}
						result.add(part.getName(), new MultipartFileResource(part.getInputStream(), part.getSubmittedFileName()));
					}
				}
			}catch (Exception ex){

			}
		}

		return result;

	}

	private void addResponseHeaders(HttpServletResponse servletResponse,ResponseEntity responseEntity) {
		HttpHeaders httpHeaders = responseEntity.getHeaders();
		for (Map.Entry<String, List<String>> entry : httpHeaders.entrySet()) {
			String headerName = entry.getKey();
			if(headerName.equals("Connection"))
				continue;
			List<String> headerValues = entry.getValue();
			for (String headerValue : headerValues) {
				servletResponse.addHeader(headerName, headerValue);
			}
		}
	}

	private void writeResponse(HttpServletResponse servletResponse,ResponseEntity responseEntity) throws Exception {
		if (servletResponse.getCharacterEncoding() == null) {
			servletResponse.setCharacterEncoding("UTF-8");
		}
		if (responseEntity.hasBody()) {
			byte[] body = (byte[]) responseEntity.getBody();
			servletResponse.setStatus(responseEntity.getStatusCodeValue());
			ServletOutputStream outputStream = servletResponse.getOutputStream();
			outputStream.write(body);
			outputStream.flush();
		}
	}
}